﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace Modelica.Annotations {

    #region Common Definitions

    
    //type DrawingUnit = Real(final unit="mm");
    //  double

    //type Point = DrawingUnit[2] "{x, y}";
    //public struct Pointm {
    //    public static readonly Pointm Default;

    //    static Pointm() {
    //        Pointm.Default = new Pointm(0, 0);
    //    }

    //    public double X { get; set; }
    //    public double Y { get; set; }

    //    public Pointm(double x, double y)
    //        : this() {
    //        this.X = x;
    //        this.Y = y;
    //    }
    //}

    //type Extent = Point[2]
    //"Defines a rectangular area {{x1, y1}, {x2, y2}}";
    public struct Extent {
        public static readonly Extent Default;

        static Extent() {
            Extent.Default = new Extent(-1, -1, 1, 1);
        }

        public Extent(Point p1, Point p2)
            : this() {
            this.P1 = p1;
            this.P2 = p2;
        }

        public Extent(double x1, double y1, double x2, double y2)
            : this(new Point(x1, y1), new Point(x2, y2)) {
        }

        public Point P1 { get; set; }
        public Point P2 { get; set; }

        public double X1 {
            get { return P1.X; }
            set { P1 = new Point(value, P1.Y); }
        }
        public double Y1 {
            get { return P1.Y; }
            set { P1 = new Point(P1.X, value); }
        }
        public double X2 {
            get { return P2.X; }
            set { P2 = new Point(value, P2.Y); }
        }
        public double Y2 {
            get { return P2.Y; }
            set { P2 = new Point(P2.X, value); }
        }
    }

    //partial record GraphicItem
    //Boolean visible = true;
    //Point origin = {0, 0};
    //Real rotation(quantity="angle", unit="deg")=0;
    //end GraphicItem;
    public abstract class GraphicItem : INotifyPropertyChanged, ICloneable {
        public GraphicItem() {
            this.Visible = true;
            this.Origin = new Point(0, 0);
            this.Rotation = 0;
        }

        /// <summary>
        /// Visible attribute indicates, if the entity should be shown.
        /// </summary>
        public bool Visible { get; set; }
        /// <summary>
        /// The origin attribute specifies the origin of the graphical
        /// item in the coordinate system of the layer in which it is defined.
        /// The origin is used to define the geometric information of the item
        /// and for all transformations applied to the item. All geometric
        /// information is given relative the origin attribute, which by
        /// default is {0, 0}.
        /// </summary>
        public Point Origin { get; set; }
        /// <summary>
        /// The rotation attribute specifies the rotation of the graphical
        /// item around the point defined by the origin attribute.
        /// </summary>
        public double Rotation { get; set; }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string info) {
            if (PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion

        #region ICloneable Members

        public abstract object Clone();

        #endregion
    }

    #region Coordinate Systems

    //record CoordinateSystem
    //Extent extent;
    //Boolean preserveAspectRatio=true;
    //Real initialScale = 0.1;
    //DrawingUnit grid[2];
    //end CoordinateSystem;
    public class CoordinateSystem {
        public CoordinateSystem(Extent Extent) {
            this.Extent = Extent;
            this.PreserveAspectRation = true;
            this.InitialScale = 0.1;
            this.Grid = new double[2];
        }
        public CoordinateSystem()
            : this(Extent.Default) {
        }

        public Extent Extent { get; set; }
        /// <summary>
        /// The attribute preserveAspectRatio specifies a constraint on the
        /// shape of components of the class. If preserveAspectRatio is true,
        /// changing the extent of components shall preserve the aspect
        /// ratio of the coordinate system of the class.
        /// </summary>
        public bool PreserveAspectRation { get; set; }
        /// <summary>
        /// The attribute initialScale specifies the default component size
        /// as initialScale times the size of the of the coordinate system 
        /// of the class. An application may use a different default value
        /// of initialScale.
        /// </summary>
        public double InitialScale { get; set; }
        /// <summary>
        /// The attribute grid specifies the spacing between grid points which
        /// can be used by tools for alignment of points in the coordinate
        /// system [e.g. “snap-to-grid”]. Its use and default value is
        /// tool-dependent.
        /// </summary>
        public double[] Grid { get; set; }
    }

    //record Icon "Representation of the icon layer"
    //CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}});
    //GraphicItem[:] graphics;
    //end Icon;
    public class Icon {
        public Icon(CoordinateSystem CoordinateSystem, List<GraphicItem> Graphics) {
            this.CoordinateSystem = CoordinateSystem;
            this.Graphics = Graphics;
        }
        public Icon()
            : this(new CoordinateSystem(), new List<GraphicItem>()) {
        }

        public CoordinateSystem CoordinateSystem { get; set; }
        public List<GraphicItem> Graphics { get; set; }

        //public List<GraphicItem> Members {
        //    get {
        //        return Graphics;
        //    }
        //}

        //public void Push(GraphicItem gi) {
        //    Graphics.Add(gi);
        //}

        //public void Push(int i, GraphicItem gi) {
        //    Graphics.Insert(i, gi);
        //}

        //public void Pop(GraphicItem gi) {
        //    if (Graphics.Contains(gi)) {
        //        Graphics.Remove(gi);
        //    } else {
        //        throw new Exception("can not remove this gi, beacouse it's not in this collection");
        //    }
        //}

    }

    //record Diagram "Representation of the diagram layer"
    //CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}});
    //GraphicItem[:] graphics;
    //end Diagram;
    public class Diagram {
        public Diagram(CoordinateSystem CoordinateSystem, List<GraphicItem> Graphics) {
            this.CoordinateSystem = CoordinateSystem;
            this.Graphics = Graphics;
        }
        public Diagram()
            : this(new CoordinateSystem(), new List<GraphicItem>()) {
        }

        public CoordinateSystem CoordinateSystem { get; set; }
        public List<GraphicItem> Graphics { get; set; }
    }

    #endregion Coordinate Systems

    #region Graphical Properties

    //type Color = Integer[3](min=0, max=255) "RGB representation";
    //public struct Colorm {
    //    public static readonly Colorm Default;

    //    static Colorm() {
    //        Colorm.Default = new Colorm(0, 0, 0);
    //    }

    //    public static Colorm FromRgb(byte r, byte g, byte b) {
    //        return new Colorm(r, g, b);
    //    }

    //    public byte R { get; set; }
    //    public byte G { get; set; }
    //    public byte B { get; set; }

    //    public Colorm(byte r, byte g, byte b)
    //        : this() {
    //        this.R = r;
    //        this.G = g;
    //        this.B = b;
    //    }
    //}

    //constant Color Black = zeros(3);
    //  Color.Default

    //type LinePattern = enumeration(None, Solid, Dash, Dot, DashDot, DashDotDot);
    public enum LinePattern {
        None,
        Solid,
        Dash,
        Dot,
        DashDot,
        DashDotDot
    };

    //type FillPattern = enumeration(None, Solid, Horizontal, Vertical,
    //Cross, Forward, Backward, CrossDiag, HorizontalCylinder,
    //VerticalCylinder, Sphere);
    /// <summary>
    /// The FillPattern attributes Horizontal, Vertical, Cross, Forward, 
    /// Backward and CrossDiag specify fill patterns drawn with the line 
    /// color over the fill color.
    /// 
    /// The attributes HorizontalCylinder, VerticalCylinder and Sphere specify
    /// gradients that represent a horizontal cylinder, a vertical cylinder
    /// and a sphere, respectively. The gradient goes from line color to fill
    /// color.
    /// </summary>
    public enum FillPattern {
        None,
        Solid,
        Horizontal,
        Vertical,
        Cross,
        Forward,
        Backward,
        CrossDiag,
        HorizontalCylinder,
        VerticalCylinder,
        Sphere
    };

    //type BorderPattern = enumeration(None, Raised, Sunken, Engraved);
    /// <summary>
    /// The border pattern attributes Raised, Sunken and Engraved represent
    /// frames which are rendered in a tool-dependent way.
    /// </summary>
    public enum BorderPattern {
        None,
        Raised,
        Sunken,
        Engraved
    };

    //type Smooth = enumeration(None, Bezier);
    /// <summary>
    /// The smooth attribute specifies that a line can be drawn as straight
    /// line segments (None) or using a spline (Bezier), where the line’s 
    /// points specify control points of a quadratic Bezier curve.
    /// 
    /// For lines with only two points, the smooth attribute has no effect.
    /// 
    /// For lines with three or more points (P1, P2, …, Pn), the middle point
    /// of each line segment (P12, P23, …, P(n-1)n) becomes the starting point
    /// and ending points of each quadratic Bezier curve. For each quadratic
    /// Bezier curve, the common point of the two line segment becomes the
    /// control point. For instance, point P2 becomes the control point for
    /// the Bezier curve starting at P12 and ending at P23. A straight line 
    /// is drawn between the starting point of the line and the starting point
    /// of the first quadratic Bezier curve, as well as between the ending
    /// point of the line and the ending point of the last quadratic Bezier
    /// curve.
    /// </summary>
    public enum Smooth {
        None,
        Bezier
    };

    //type Arrow = enumeration(None, Open, Filled, Half);
    public enum Arrow {
        None,
        Open,
        Filled,
        Half
    };

    //type TextStyle = enumeration(Bold, Italic, UnderLine);
    public enum TextStyle {
        Bold,
        Italic,
        Underline
    };

    //type TextAlignment = enumeration(Left, Center, Right);
    public enum TextAlignment {
        Left,
        Center,
        Right
    };

    //record FilledShape "Style attributes for filled shapes"
    //Color lineColor = Black "Color of border line";
    //Color fillColor = Black "Interior fill color";
    //LinePattern pattern = LinePattern.Solid "Border line pattern";
    //FillPattern fillPattern = FillPattern.None "Interior fill pattern";
    //DrawingUnit lineThickness = 0.25 "Line thickness";
    //end FilledShape;
    public class FilledShape : GraphicItem {
        public FilledShape(
            Color LineColor, 
            Color FillColor, 
            LinePattern Pattern,
            FillPattern FillPattern, 
            double LineThickness) {
            this.LineColor = LineColor;
            this.FillColor = FillColor;
            this.Pattern = Pattern;
            this.FillPattern = FillPattern;
            this.LineThickness = LineThickness;
        }
        public FilledShape()
            : this(Colors.Black, Colors.Black, LinePattern.Solid, FillPattern.None, 0.25) {
        }

        public Color LineColor { get; set; }
        public Color FillColor { get; set; }
        public LinePattern Pattern { get; set; }
        public FillPattern FillPattern { get; set; }
        public double LineThickness { get; set; }

        #region ICloneable Members

        public override object Clone() {
            return new FilledShape(this.LineColor, this.FillColor, this.Pattern, this.FillPattern,
                this.LineThickness);
        }

        #endregion
    }

    #endregion Graphical Properties

    #endregion Common Definitions

    #region Component Instance

    //TODO toto je zobrane z Modelicy 3.0 ?
    // lebo omc vracia nieco trochu ine podla mna... viz subor annotations.mo, ktory 
    // je v nejako maily od pavla v konferencii -> prerobim to podla neho zatial
    //record Transformation
    //Point origin = {0, 0};
    //Extent extent;
    //Real rotation(quantity="angle", unit="deg")=0;
    //end Transformation;
    /// <summary>
    /// The origin attribute defines the position of the component in
    /// the coordinate system of the enclosing class. The extent defines
    /// the position, size and flipping of the component, relative to
    /// the origin attribute. The extent is defined relative to the origin
    /// attribute of the component instance. Given an extent 
    /// {{x1, y1}, {x2, y2}}, x2 &lt; x1 defines horizontal flipping and 
    /// y2 &lt; y1 defines vertical flipping around the point defined by 
    /// the origin attribute.
    /// 
    /// The rotation attribute specifies rotation of the extent around 
    /// the point defined by the origin attribute.
    /// </summary>
    public class Transformation {
        public Transformation(Point Origin, Extent Extent, double Rotation) {
            this.Origin = Origin;
            this.Extent = Extent;
            this.Rotation = Rotation;
        }
        public Transformation()
            : this(new Point(0, 0), Extent.Default, 0) {
        }

        public Transformation(Point Origin,
            double Scale,
            double AspectRatio,
            bool FlipHorizontal,
            bool FlipVertical,
            double Rotation) {
            this.Origin = Origin;
            this.Scale = Scale;
            this.AspectRatio = AspectRatio;
            this.FlipHorizontal = FlipHorizontal;
            this.FlipVertical = FlipVertical;
            this.Rotation = Rotation;
        }

        /// <summary>
        /// Possible difference between Modelica 3.0 and 2.2 (omc) -> in 2.2 this value
        /// represents the middle of the icon "extent"
        /// </summary>
        public Point Origin { get; set; }
        public Extent Extent { get; set; }

        /// <summary>
        /// Deprecated - Modelica 2.2 construct, in Modelica 3.0 all info is stored in Extent
        /// </summary>
        public double Scale { get; set; }

        /// <summary>
        /// Deprecated - Modelica 2.2 construct, in Modelica 3.0 all info is stored in Extent
        /// </summary>
        public double AspectRatio { get; set; }

        /// <summary>
        /// Deprecated - Modelica 2.2 construct, in Modelica 3.0 all info is stored in Extent
        /// </summary>
        public bool FlipHorizontal { get; set; }

        /// <summary>
        /// Deprecated - Modelica 2.2 construct, in Modelica 3.0 all info is stored in Extent
        /// </summary>
        public bool FlipVertical { get; set; }

        public double Rotation { get; set; }
    }

    //record Placement
    //Boolean visible = true;
    //Transformation transformation "Placement in the dagram layer";
    //Transformation iconTransformation "Placement in the icon layer";
    //end Placement;
    public class Placement {
        public Placement(bool Visible, Transformation Transformation, Transformation IconTransformation) {
            this.Visible = Visible;
            this.Transformation = Transformation;
            this.IconTransformation = IconTransformation;
        }
        public Placement()
            : this(true, new Transformation(), new Transformation()) {
        }

        public bool Visible { get; set; }
        public Transformation Transformation { get; set; }
        public Transformation IconTransformation { get; set; }
    }

    #endregion Component Instance

    #region Extends clause

    //record IconMap
    //Extent extent = {{0, 0}, {0, 0}};
    //Boolean primitivesVisible = true;
    //end IconMap;
    public class IconMap {
        public IconMap(Extent Extent, bool PrimitivesVisible) {
            this.Extent = Extent;
            this.PrimitivesVisible = PrimitivesVisible;
        }
        public IconMap()
            : this(new Extent(0, 0, 0, 0), true) {
        }

        public Extent Extent { get; set; }
        public bool PrimitivesVisible { get; set; }
    }

    //record DiagramMap
    //Extent extent = {{0, 0}, {0, 0}};
    //Boolean primitivesVisible = true;
    //end DiagramMap;
    public class DiagramMap {
        public DiagramMap(Extent Extent, bool PrimitivesVisible) {
            this.Extent = Extent;
            this.PrimitivesVisible = PrimitivesVisible;
        }
        public DiagramMap()
            : this(new Extent(0, 0, 0, 0), true) {
        }

        public Extent Extent { get; set; }
        public bool PrimitivesVisible { get; set; }
    }

    #endregion Extends clause

    #region Graphical primitives

    //record Line
    //extends GraphicItem;
    //Point points[:];
    //Color color = Black;
    //LinePattern pattern = LinePattern.Solid;
    //DrawingUnit thickness = 0.25;
    //Arrow arrow[2] = {Arrow.None, Arrow.None}; "{start arrow, end arrow}"
    //DrawingUnit arrowSize=3;
    //Smooth smooth = Smooth.None "Spline";
    //end Line;
    public class Line : GraphicItem {
        public Line(
            Point[] Points,
            Color Color,
            LinePattern Pattern,
            double Thickness,
            Arrow[] Arrow,
            double ArrowSize,
            Smooth Smooth) {

            this.Points = (Point[])Points.Clone();
            this.Color = Color;
            this.Pattern = Pattern;
            this.Thickness = Thickness;
            this.Arrow = (Arrow[])Arrow.Clone();
            this.ArrowSize = ArrowSize;
            this.Smooth = Smooth;
        }
        public Line()
            : this(new Point[2], Colors.Black, LinePattern.Solid,
            0.25, new Arrow[2], 3.0, Smooth.None) {
        }

        public Point[] Points { get; set; }
        public Color Color { get; set; }
        public LinePattern Pattern { get; set; }
        public double Thickness { get; set; }
        public Arrow[] Arrow { get; set; }
        public double ArrowSize { get; set; }
        public Smooth Smooth { get; set; }
        
        #region ICloneable Members

        public override object Clone() {
            return new Line(this.Points, this.Color, this.Pattern, this.Thickness, 
                this.Arrow, this.ArrowSize, this.Smooth);
        }
        
        #endregion
    }

    //record Polygon
    //extends GraphicItem;
    //extends FilledShape;
    //Point points[:];
    //Smooth smooth = Smooth.None "Spline outline";
    //end Polygon;
    public class Polygon : FilledShape {
        public Polygon(
            Color LineColor,
            Color FillColor,
            LinePattern Pattern,
            FillPattern FillPattern,
            double LineThickness,
            Point[] Points,
            Smooth Smooth)
            : base(LineColor, FillColor, Pattern, FillPattern, LineThickness) {
            this.Points = (Point[])Points.Clone();
            this.Smooth = Smooth;
        }
        public Polygon()
            : this(Colors.Black, Colors.Black, LinePattern.Solid, FillPattern.None,
            0.25, new Point[2], Smooth.None) {
        }

        public Point[] Points { get; set; }
        public Smooth Smooth { get; set; }

        #region ICloneable Members

        public override object Clone() {
            return new Polygon(this.LineColor, this.FillColor, this.Pattern, this.FillPattern,
                this.LineThickness, this.Points, this.Smooth);
        }

        #endregion
    }

    //record Rectangle
    //extends GraphicItem;
    //extends FilledShape;
    //BorderPattern borderPattern = BorderPattern.None;
    //Extent extent;
    //DrawingUnit radius = 0 "Corner radius";
    //end Rectangle;
    public class Rectangle : FilledShape {
        public Rectangle(
            Color LineColor,
            Color FillColor,
            LinePattern Pattern,
            FillPattern FillPattern,
            double LineThickness,
            BorderPattern BorderPattern,
            Extent Extent,
            double Radius)
            : base(LineColor, FillColor, Pattern, FillPattern, LineThickness) {
            this.BorderPattern = BorderPattern;
            this.Extent = Extent;
            this.Radius = Radius;
        }
        public Rectangle()
            : this(Colors.Black, Colors.Black, LinePattern.Solid, FillPattern.None,
            0.25, BorderPattern.None, Extent.Default, 0) {
        }

        public BorderPattern BorderPattern { get; set; }
        public Extent Extent;
        public double Radius { get; set; }

        #region ICloneable Members

        public override object Clone() {
            return new Rectangle(this.LineColor, this.FillColor, this.Pattern, this.FillPattern,
                this.LineThickness, this.BorderPattern, this.Extent, this.Radius);
        }

        #endregion
    }

    //record Ellipse
    //extends GraphicItem;
    //extends FilledShape;
    //Extent extent;
    //Real startAngle(quantity="angle", unit="deg")=0;
    //Real endAngle(quantity="angle", unit="deg")=360;
    //end Ellipse;
    public class Ellipse : FilledShape {
        public Ellipse(
            Color LineColor,
            Color FillColor,
            LinePattern Pattern,
            FillPattern FillPattern,
            double LineThickness,
            Extent Extent)
            : base(LineColor, FillColor, Pattern, FillPattern, LineThickness) {
            this.Extent = Extent;
            this.StartAngle = 0;
            this.EndAngle = 360;
        }
        public Ellipse()
            : this(Colors.Black, Colors.Black, LinePattern.Solid, FillPattern.None,
            0.25, Extent.Default) {
        }

        public Extent Extent;
        public double StartAngle { get; set; }
        public double EndAngle { get; set; }
        
        #region ICloneable Members

        public override object Clone() {
            return new Ellipse(this.LineColor, this.FillColor, this.Pattern, this.FillPattern,
                this.LineThickness, this.Extent);
        }

        #endregion
    }

    //record Text
    //extends GraphicItem;
    //extends FilledShape;
    //Extent extent;
    //String textString;
    //Real fontSize = 0 "unit pt";
    //String fontName;
    //TextStyle textStyle[:];
    //TextAlignment horizontalAlignment = TextAlignment.Center;
    //end Text;
    public class Text : FilledShape {
        public Text(
            Color LineColor,
            Color FillColor,
            LinePattern Pattern,
            FillPattern FillPattern,
            double LineThickness,
            Extent Extent,
            String TextString,
            double FontSize,
            String FontName,
            List<TextStyle> TextStyle)
            : base(LineColor, FillColor, Pattern, FillPattern, LineThickness) {
            this.Extent = Extent;
            this.TextString = TextString;
            this.FontSize = FontSize;
            this.FontName = FontName;
            this.TextStyle = new List<TextStyle>(TextStyle);
            this.HorizontalAlignment = TextAlignment.Center;
        }
        public Text()
            : this(Colors.Black, Colors.Black, LinePattern.Solid, FillPattern.None,
            0.25, Extent.Default, "", 0, "", new List<TextStyle>()) {
        }

        public Extent Extent;
        public String TextString { get; set; }
        public double FontSize { get; set; }
        public String FontName { get; set; }
        public List<TextStyle> TextStyle { get; set; }
        public TextAlignment HorizontalAlignment { get; set; }

        #region ICloneable Members

        public override object Clone() {
            return new Text(this.LineColor, this.FillColor, this.Pattern, this.FillPattern,
                this.LineThickness, this.Extent, this.TextString, 
                this.FontSize, this.FontName, this.TextStyle);
        }

        #endregion
    }

    //record Bitmap
    //extends GraphicItem;
    //Extent extent;
    //String fileName "Name of bitmap file";
    //String imageSource "Base64 representation of bitmap";
    //end Bitmap;
    public class Bitmap : GraphicItem {
        public Bitmap(
            Extent Extent,
            String FileName,
            String ImageSource) {
            this.Extent = Extent;
            this.FileName = FileName;
            this.ImageSource = ImageSource;
        }
        public Bitmap()
            : this(Extent.Default, "", "") {
        }

        public Extent Extent;
        public String FileName { get; set; }
        public String ImageSource { get; set; }

        #region ICloneable Members

        public override object Clone() {
            return new Bitmap(this.Extent, this.FileName, this.ImageSource);
        }

        #endregion
    }

    #endregion Graphical primitives
}
